home *** CD-ROM | disk | FTP | other *** search
- /*
- File: AltPoly.cpp
-
- Contains: OpenDoc polygon: optional C++ savvy classes
-
- Owned by: Jens Alfke
-
- Copyright: © 1993 - 1995 by Apple Computer, Inc., all rights reserved.
-
- To Do:
-
- Improve the equality tests for contours and polygons. See comments
- in the two "operator==" methods for details.
-
- In Progress:
-
- */
-
-
- #ifndef _ALTPOINT_
- #include "AltPoint.h" // Use C++ savvy ODPoint and ODRect
- #endif
-
- #ifndef _ALTPOLY_
- #include "AltPoly.h"
- #endif
-
- #ifndef SOM_ODTransform_xh
- #include "Trnsform.xh"
- #endif
-
- #ifndef _LINEOPS_
- #include "LineOps.h"
- #endif
-
- #ifndef _ODMEMORY_
- #include "ODMemory.h"
- #endif
-
- #ifndef _EXCEPT_
- #include "Except.h"
- #endif
-
- #ifndef SOM_ODStorageUnit_xh
- #include "StorageU.xh"
- #endif
-
- #ifndef _STDTYPES_
- #include "StdTypes.xh"
- #endif
-
- #ifndef _EXCEPT_
- #include "Except.h"
- #endif
-
- #ifndef _ODDEBUG_
- #include "ODDebug.h"
- #endif
-
- #ifndef _STORUTIL_
- #include <StorUtil.h>
- #endif
-
- #ifndef _STDTYPIO_
- #include <StdTypIO.h>
- #endif
-
- #ifndef _UTILERRS_
- #include "UtilErrs.h"
- #endif
-
- #ifdef _PLATFORM_MACINTOSH_
- #ifndef __GXERRORS__
- #include "GXErrors.h"
- #endif
- #ifndef __GXGRAPHICS__
- #include "GXGraphics.h"
- #endif
- #endif
-
- #include <stddef.h> // Defines offsetof() macro
-
- #ifndef _ODDEBUG_
- #include "ODDebug.h"
- #endif
-
-
- const ODSLong kMaxLong = 0x7FFFFFFF;
-
- #pragma segment ODShape
-
-
- //==============================================================================
- // Destructos
- //==============================================================================
-
-
- ODTempPolygon::ODTempPolygon( )
- {
- // This constructor doesn't do anything special, but if not declared it will
- // be inlined at the call site, resulting in lots of extra code due to the
- // multiple inheritance.
- }
-
-
- ODTempPolygon::~ODTempPolygon( )
- {
- this->Clear();
- }
-
-
- ODTempPolygonPtr::ODTempPolygonPtr( )
- :fPoly(kODNULL)
- {
- }
-
-
- ODTempPolygonPtr::ODTempPolygonPtr( ODPolygon *p )
- :fPoly(p)
- {
- }
-
-
- ODTempPolygonPtr::~ODTempPolygonPtr( )
- {
- delete fPoly;
- fPoly = kODNULL;
- }
-
-
- TempGXShape::TempGXShape( )
- :fShape(kODNULL)
- {
- }
-
-
- TempGXShape::TempGXShape( gxShape s )
- :fShape(s)
- {
- }
-
-
- TempGXShape::~TempGXShape( )
- {
- if( fShape ) {
- GXDisposeShape(fShape);
- fShape = kODNULL;
- }
- }
-
-
- //==============================================================================
- // QuickDraw GX Utilities
- //==============================================================================
-
-
- #pragma segment QDGXShape
-
-
- static void
- ClearGXError( )
- {
- GXGetGraphicsError(kODNULL);
- // GX error status is cleared after asking for errors.
- }
-
-
- static void
- ThrowIfGXError( )
- {
- gxGraphicsError err = GXGetGraphicsError(kODNULL); // Get latest graphics error
- if( err )
- THROW(err,"QuickDraw GX error");
- }
-
-
- static void
- ThrowIfFirstGXError( )
- {
- gxGraphicsError err;
- (void) GXGetGraphicsError(&err); // Get first error, not last
- if( err )
- THROW(err,"QuickDraw GX error");
- }
-
-
- /******************************************************************************/
- //** ALLOCATION
- /******************************************************************************/
-
-
- ODPolygon::ODPolygon( )
- :_maximum(0),
- _length(0),
- _buf(kODNULL)
- {
- }
-
-
- #if ODDebug
- ODPolygon::~ODPolygon( )
- {
- // To help catch double-deletes of ODPolygon structures!
- _buf = (ODPolygonData*)0xDDDDDDDD;
- _length = _maximum = 0xDDDDDDDD;
- }
- #endif
-
-
- void
- ODPolygon::Delete( )
- {
- ODDisposePtr(_buf);
- delete this;
- }
-
-
- void
- ODPolygon::Clear( )
- {
- ODDisposePtr(_buf);
- _buf = kODNULL;
- _length = _maximum = 0;
- }
-
-
- static ODULong
- CalcDataSize( ODSLong nVertices )
- {
- if( nVertices==0 )
- return 0;
- else
- return offsetof(ODPolygonData,firstContour.vertex[nVertices]);
- }
-
-
- void
- ODPolygon::Realloc( ODULong dataSize )
- {
- if( _buf!=kODNULL && dataSize>=_length && dataSize<=_maximum )
- _length = dataSize;
- else {
- ODPtr newData;
- if( dataSize!=0 )
- newData = ODNewPtr(dataSize, kDefaultHeapID);
- ODDisposePtr(_buf);
- if( dataSize!=0 )
- _buf = (ODPolygonData*)newData;
- else
- _buf = kODNULL;
- _length = _maximum = dataSize;
- }
- }
-
-
- void
- ODPolygon::SetData( const ODPolygonData *data )
- {
- _length = sizeof(ODULong) * (1+data->nContours);
- const ODContour *c = &data->firstContour;
- for( ODULong i=data->nContours; i!=0; i-- ) {
- _length += c->nVertices * sizeof(ODPoint);
- c = c->NextContour();
- }
-
- ODDisposePtr(_buf);
- _buf = (ODPolygonData*)data;
- _maximum = _length;
- }
-
-
- ODPolygon*
- ODPolygon::SetNVertices( ODSLong nVertices )
- {
- ASSERT(nVertices>=0,kODErrValueOutOfRange);
-
- this->Realloc(CalcDataSize(nVertices));
- if( nVertices>0 ) {
- _buf->nContours = 1;
- _buf->firstContour.nVertices = nVertices;
- }
- return this;
- }
-
-
- ODPolygon*
- ODPolygon::SetVertices( ODSLong nVertices, const ODPoint *vertices )
- {
- ASSERT(nVertices>=0,kODErrValueOutOfRange);
- ASSERT(vertices!=kODNULL,kODErrIllegalNullInput);
-
- this->SetNVertices(nVertices);
- if( nVertices>0 )
- ODBlockMove( (void *) vertices, _buf->firstContour.vertex, nVertices*sizeof(ODPoint) );
- return this;
- }
-
-
- ODPolygon*
- ODPolygon::SetContours( ODSLong nContours, const ODSLong *contourVertices )
- {
- ASSERT(nContours>=0,kODErrValueOutOfRange);
- if( nContours==0 )
- return this->SetNVertices(0);
- else {
- ASSERT(contourVertices!=kODNULL,kODErrIllegalNullInput);
- ODULong totalVertices = 0;
- ODSLong i;
- for( i=nContours-1; i>=0; i-- )
- totalVertices += contourVertices[i];
- this->Realloc( offsetof(ODPolygonData,firstContour)
- + offsetof(ODContour,vertex[0]) * nContours
- + sizeof(ODPoint)*totalVertices );
- _buf->nContours = nContours;
- ODContour *cont = this->FirstContour();
- for( i=0; i<nContours; i++ ) {
- cont->nVertices = contourVertices[i];
- cont = cont->NextContour();
- }
- return this;
- }
- }
-
-
- ODPolygon*
- ODPolygon::SetRect( const ODRect &r )
- {
- if( r.IsEmpty() )
- return this->SetNVertices(0);
- else {
- this->SetNVertices(4);
- _buf->firstContour.vertex[0] = r.TopLeft();
- _buf->firstContour.vertex[1].Set(r.right,r.top);
- _buf->firstContour.vertex[2] = r.BotRight();
- _buf->firstContour.vertex[3].Set(r.left,r.bottom);
- }
- return this;
- }
-
-
- ODPolygon*
- ODPolygon::CopyFrom( const ODPolygon &poly )
- {
- if( poly._buf != _buf ) {
- ODULong size = poly.GetDataSize();
- this->Realloc(size);
- ODBlockMove(poly.GetData(),_buf,size);
- }
- return this;
- }
-
-
- ODPolygon*
- ODPolygon::MoveFrom( ODPolygon &poly )
- {
- if( poly._buf != _buf ) {
- ODDisposePtr(_buf);
- _buf = poly._buf;
- _length = poly._length;
- _maximum = poly._maximum;
- }
- if( &poly._buf != &_buf ) { // Don't clear poly if it's myself!
- poly._buf = kODNULL;
- poly._length = poly._maximum = 0;
- }
- return this;
- }
-
-
-
- #ifdef _PLATFORM_MACINTOSH_
- #pragma segment ODGXShape
-
- ODPolygon*
- ODPolygon::CopyFrom( gxShape shape )
- {
- ClearGXError();
- TempGXShape copiedShape = GXCopyToShape(kODNULL,shape);
- GXPrimitiveShape(copiedShape);
- GXSimplifyShape(copiedShape);
- GXSetShapeType(copiedShape,gxPolygonType);
- ThrowIfFirstGXError();
-
- ODULong size = GXGetPolygonParts(copiedShape, 1,gxSelectToEnd, kODNULL);
- ThrowIfGXError();
- this->Realloc(size);
- GXGetPolygonParts(copiedShape, 1,gxSelectToEnd, (gxPolygons*)_buf);
- ThrowIfGXError();
-
- return this;
- }
- #pragma segment ODShape
- #endif
-
-
- ODPolygon*
- ODPolygon::ReadFrom( Environment *ev, ODStorageUnit *su )
- {
-
- if( !su->Exists(ev,kODNULL,kODPolygon,kODPosUndefined) ) {
- this->Clear();
- } else {
- ODPropertyName propName = su->GetProperty(ev);
- ODGetPolygonProp(ev, su, propName, kODPolygon, this);
- ODDisposePtr((ODPtr) propName);
- }
-
- return this;
- }
-
-
- ODPolygon*
- ODPolygon::WriteTo( Environment *ev, ODStorageUnit *su ) const
- {
- ODPropertyName propName = su->GetProperty(ev);
- ODSetPolygonProp(ev, su, propName, kODPolygon, this);
- ODDisposePtr((ODPtr) propName);
-
- return (ODPolygon*)this;
- }
-
-
- /******************************************************************************/
- //** POLYGON STUFF
- /******************************************************************************/
-
-
- ODSLong
- ODPolygon::GetNContours( ) const
- {
- return _length>0 ?_buf->nContours :0;
- }
-
-
- const ODContour*
- ODPolygon::FirstContour( ) const
- {
- return _length>0 ?&_buf->firstContour :kODNULL;
- }
-
-
- ODContour*
- ODPolygon::FirstContour( )
- {
- return _length>0 ?&_buf->firstContour :kODNULL;
- }
-
-
- ODPolygon*
- ODPolygon::Copy( ) const
- {
- ODTempPolygonPtr poly = new ODPolygon;
- poly->CopyFrom(*this);
- return poly.DontDelete();
- }
-
-
- void
- ODPolygon::ComputeBoundingBox( ODRect *bbox ) const
- {
- ASSERT(bbox!=kODNULL,kODErrIllegalNullInput);
-
- if( _buf==kODNULL || _buf->nContours <= 0 ) {
- bbox->Clear();
- return;
- }
-
- // Start bbox out as maximally empty:
- bbox->left = bbox->top = kMaxLong;
- bbox->right = bbox->bottom = -kMaxLong;
-
- const ODContour *c = this->FirstContour();
- for( ODSLong i=this->GetNContours(); i>0; i-- ) {
- ODPoint *pt = (ODPoint*)c->vertex;
- for( ODSLong v=c->nVertices; v>0; v--,pt++ ) {
- if( pt->x < bbox->left ) bbox->left = pt->x;
- if( pt->x > bbox->right ) bbox->right = pt->x;
- if( pt->y < bbox->top ) bbox->top = pt->y;
- if( pt->y > bbox->bottom ) bbox->bottom= pt->y;
- }
- if( i>1 )
- c = c->NextContour();
- }
- }
-
-
- ODBoolean
- ODContour::operator== ( const ODContour &cont ) const
- {
- /* This test is complicated by the fact that the two contours might have the
- same points, but out of phase. Therefore we have to compare the points
- in sequence, once per possible phase difference. */
-
- ODSLong nv = this->nVertices;
- if( nv != cont.nVertices )
- return kODFalse;
-
- for( ODSLong phase=0; phase<nv; phase++ ) {
- const ODPoint *p0 = &this->vertex[0];
- const ODPoint *p1 = &cont.vertex[phase];
- ODSLong i;
- for( i=nVertices; i>0; i-- ) {
- if( i==phase )
- p1 = &cont.vertex[0];
- if( ! (p0++)->ApproxEquals(*p1++) ) // Coords may differ very slightly
- break;
- }
- if( i==0 )
- return kODTrue;
- }
- return kODFalse;
- }
-
-
- Boolean
- ODPolygon::operator== ( ODPolygon &poly ) const
- {
- /* This test is complicated by the fact that the two polygons may not have their
- contours in the same order. Our approach is to step through my contours in
- order, trying to match each to a unique contour in the target. To ensure
- uniqueness, the sign of the nVertices field in a target contour is flipped
- after it's matched. */
-
- if( this->GetNContours() != poly.GetNContours() )
- return kODFalse;
- if( &poly == this )
- return kODTrue;
-
- ODBoolean result = kODTrue;
- const ODContour * c = this->FirstContour();
- ODContour *pc;
-
- ODSLong i;
- for( i=this->GetNContours(); i>0; i-- ) {
- pc = poly.FirstContour();
- ODSLong j;
- for( j=poly.GetNContours(); j>0; j-- ) {
- if( pc->nVertices>0 && *c==*pc ) { // Compare contours!
- pc->nVertices = -pc->nVertices; // Use sign bit as a flag (yech)
- break;
- }
- if( j>1 )
- pc = pc->NextContour();
- }
- if( j<=0 ) {
- result = kODFalse; // No match for contour
- break;
- }
-
- if( i>1 )
- c = c->NextContour();
- }
-
- // Now that we know, clear all the sign bits:
- pc = poly.FirstContour();
- for( i=poly.GetNContours(); i>0; i-- ) {
- if( pc->nVertices<0 )
- pc->nVertices = -pc->nVertices;
- pc = pc->NextContour();
- }
- return result;
- }
-
-
- Boolean
- ODPolygon::IsEmpty( ) const
- {
- // FIX: This is not very smart. It will probably be necessary to compute the area
- // of each contour...
-
- return _buf==kODNULL || _buf->nContours==0;
- }
-
-
- Boolean
- ODPolygon::IsRectangular( ) const
- {
- return _buf==kODNULL || _buf->nContours==0 ||
- (_buf->nContours==1 && _buf->firstContour.IsRectangular());
- }
-
-
- Boolean
- ODPolygon::AsRectangle( ODRect *r ) const
- {
- if( _buf==kODNULL || _buf->nContours==0 ) {
- r->Clear();
- return kODTrue;
- } else if( _buf->nContours==1 )
- return _buf->firstContour.AsRectangle(r);
- else
- return kODFalse;
- }
-
-
- Boolean
- ODContour::IsRectangular( ) const
- {
- if( nVertices != 4 )
- return kODFalse;
- else if( vertex[0].x == vertex[1].x ) // 1st edge is vertical
- return vertex[1].y==vertex[2].y
- && vertex[2].x==vertex[3].x
- && vertex[3].y==vertex[0].y;
- else if( vertex[0].y == vertex[1].y ) // 1st edge is horizontal
- return vertex[1].x==vertex[2].x
- && vertex[2].y==vertex[3].y
- && vertex[3].x==vertex[0].x;
- else
- return kODFalse;
- }
-
-
- Boolean
- ODContour::AsRectangle( ODRect *r ) const
- {
- ASSERT(r!=kODNULL,kODErrIllegalNullInput);
-
- if( this->IsRectangular() ) {
- ODRect r2(vertex[0],vertex[2]); // C'tor properly orders the coords
- *r = r2;
- return kODTrue;
- } else
- return kODFalse;
- }
-
-
- /******************************************************************************/
- //** REGION CONVERSION
- /******************************************************************************/
-
-
- ODBoolean
- ODContour::HasExactRegion( ) const
- {
- const ODPoint *b = &vertex[0];
- for( ODSLong i=nVertices; i>=0; i-- ) {
- const ODPoint *a = &vertex[i];
- if( (a->x & 0xFFFF) || (a->y & 0xFFFF) )
- return kODFalse; // Non-integer coordinates
- if( a->x!=b->x && a->y!=b->y )
- return kODFalse; // Diagonal line
- b = a;
- }
- return kODTrue;
- }
-
-
- ODBoolean
- ODPolygon::HasExactRegion( ) const
- {
- const ODContour *c = this->FirstContour();
- for( long i=this->GetNContours(); i>0; i-- ) {
- if( !c->HasExactRegion() )
- return kODFalse;
- if( i>1 )
- c = c->NextContour();
- }
- return kODTrue;
- }
-
-
- #ifdef _PLATFORM_MACINTOSH_
- PolyHandle
- ODContour::AsQDPolygon( ) const
- {
- ODSLong size = (sizeof(short)+sizeof(Rect)+sizeof(Point)) + nVertices*sizeof(Point);
- if( size > 32767 )
- THROW(kODErrShapeTooComplex);
- PolyHandle p = (PolyHandle) ODNewHandle(size);
- (**p).polySize = (short)size;
- Rect &bbox = (**p).polyBBox;
- SetRect(&bbox,32767,32767,-32767,-32767);
-
- const ODPoint *src = &vertex[0];
- Point *dst = &(**p).polyPoints[0];
- for( ODSLong i=nVertices; i>0; i-- ) {
- *dst = src->AsQDPoint();
- if( dst->h < bbox.left ) bbox.left = dst->h;
- if( dst->v < bbox.top ) bbox.top = dst->v;
- if( dst->h > bbox.right ) bbox.right = dst->h;
- if( dst->v > bbox.bottom) bbox.bottom= dst->v;
- src++;
- dst++;
- }
- *dst = vertex[0].AsQDPoint(); // QD makes us repeat the 1st pt at the end
-
- return p;
- }
- #endif
-
-
- #ifdef _PLATFORM_MACINTOSH_
- RgnHandle
- ODPolygon::AsQDRegion( ) const
- {
- // NOTE: This method will not work properly for self-intersecting polygons!
- // QuickDraw uses even-odd filling, while OpenDoc uses winding-number.
-
- if( !this->HasData() )
- return ODNewRgn();
-
- GrafPtr port, myPort;
-
- // Make sure we have a real port to work with:
- GetPort(&port);
- if( FrontWindow() ) {
- SetPort(FrontWindow());
- myPort = kODNULL;
- } else {
- myPort = new GrafPort;
- OpenPort(myPort);
- }
-
- RgnHandle rgn = ODNewRgn();
-
- OpenRgn();
- TRY{
- PolyHandle poly;
- const ODContour *cont = this->FirstContour();
- for( ODSLong i=_buf->nContours; i>0; i-- ) {
- poly = cont->AsQDPolygon();
- FramePoly(poly);
- KillPoly(poly);
- cont = cont->NextContour();
- }
- }CATCH_ALL{
- CloseRgn(rgn);
- DisposeRgn(rgn);
- SetPort(port);
- if( myPort ) {
- ClosePort(myPort);
- delete myPort;
- }
- RERAISE;
- }ENDTRY
-
- CloseRgn(rgn);
-
- SetPort(port);
- if( myPort ) {
- ClosePort(myPort);
- delete myPort;
- }
- return rgn;
- }
- #endif
-
-
- #ifdef _PLATFORM_MACINTOSH_
- gxShape
- ODPolygon::AsGXShape( ) const
- {
- gxShape shape;
- if( this->HasData() ) {
- shape = GXNewPolygons( (gxPolygons*)this->GetData() );
- GXSetShapeFill(shape,gxWindingFill);
- } else
- shape = GXNewShape(gxEmptyType);
-
- ThrowIfFirstGXError();
- return shape;
- }
- #endif
-
-
- void
- ODPolygon::Transform( Environment *ev, ODTransform *xform )
- {
- if( this->HasData() ) {
- ODContour *c = &_buf->firstContour;
- for( ODSLong i=_buf->nContours; i>0; i-- ) {
- ODPoint *pt = c->vertex;
- for( ODSLong v=c->nVertices; v>0; v--, pt++ )
- xform->TransformPoint(ev,pt);
- if( i>1 )
- c = c->NextContour();
- }
- }
- }
-
-
- /******************************************************************************/
- //** CONTAINMENT TEST
- /******************************************************************************/
-
-
- //------------------------------------------------------------------------------
- // ODPolygon::Contains
- //
- // Does a polygon contain a point?
- // We determine this by drawing a ray from the point to the right towards
- // infinity, and finding the polygon edges that intersect this ray. For each
- // such edge, count it as 1 if its y value is increasing, -1 if decreasing.
- // The sum of these values is 0 if the point is outside the polygon.
- //------------------------------------------------------------------------------
-
- ODSLong
- ODPolygon::Contains( ODPoint point ) const
- {
- if( !this->HasData() )
- return kODFalse;
-
- ODSLong count = 0;
- const ODPoint *pp1, *pp2;
- ODPoint p1, p2;
- ODPoint ray = point;
-
- for( PolyEdgeIterator polyIter (this); polyIter.IsNotComplete(); polyIter.Next() ) {
- polyIter.CurrentEdge(pp1,pp2);
- p1 = *pp1;
- p2 = *pp2;
-
- if( p1.y==p2.y ) { // Horizontal line: ignore
- if( p1.y==point.y && InRange(point.x, p1.x,p2.x) ) { // unless point is on it
- return 0;
- }
- } else {
- ray.x = Max(p1.x,p2.x);
- ODPoint sect;
- if( ray.x >= point.x )
- if( IntersectSegments(p1,p2, point,ray, §) ) {
- if( WithinEpsilon(point.x,sect.x) && WithinEpsilon(point.y,sect.y) ) {
- return 0;
- }
- if( p2.y > p1.y )
- count++;
- else
- count--;
- }
- }
- }
-
- return count;
- }
-
-
- /******************************************************************************/
- //** POLYGON EDGE ITERATOR
- /******************************************************************************/
-
-
- PolyEdgeIterator::PolyEdgeIterator( const ODPolygon *poly )
- :fPoly (poly)
- {
- fCurContour = poly->FirstContour();
- fCurContourIndex = 0;
- fCurVertex = 0;
- }
-
-
- void
- PolyEdgeIterator::CurrentEdge( const ODPoint* &v1, const ODPoint* &v2 )
- {
- v1 = &fCurContour->vertex[fCurVertex];
- if( fCurVertex+1 < fCurContour->nVertices )
- v2 = v1+1;
- else
- v2 = &fCurContour->vertex[0];
- }
-
-
- Boolean
- PolyEdgeIterator::Next( )
- {
- if( !fCurContour ) // Was already finished
- return false;
- if( ++fCurVertex >= fCurContour->nVertices ) // Next vertex; if past contour:
- if( ++fCurContourIndex >= fPoly->GetNContours() ) { // Next contour; if past end:
- fCurContour = kODNULL;
- return false; //...we're done.
- } else {
- fCurContour = fCurContour->NextContour(); // Else go to start of contour
- fCurVertex = 0;
- }
- return kODTrue;
- }
-
-
- Boolean
- PolyEdgeIterator::IsNotComplete( )
- {
- return fCurContour!=kODNULL;
- }
-